/*** In The Name of Allah ***/
package com.codeanalysissys.backend.python;
import com.codeanalysissys.backend.cpp.parser.CppLexer;
import com.codeanalysissys.backend.cpp.parser.CppParser;
import com.codeanalysissys.backend.golang.GoCFGBuilder;
import com.codeanalysissys.backend.graphs.cfg.CFEdge;
import com.codeanalysissys.backend.graphs.cfg.CFNode;
import com.codeanalysissys.backend.graphs.cfg.ControlFlowGraph;
import com.codeanalysissys.backend.javascript.parser.JavaScriptLexer;
import com.codeanalysissys.backend.javascript.parser.JavaScriptParser;
import com.codeanalysissys.backend.python.parser.PythonLexer;
import com.codeanalysissys.backend.python.parser.PythonParser;
import com.codeanalysissys.backend.python.parser.PythonParserBaseVisitor;
import ghaffarian.graphs.Edge;
import ghaffarian.nanologger.Logger;

import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.misc.Interval;
import org.antlr.v4.runtime.tree.ParseTree;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
/**
 * A Control Flow Graph (CFG) builder for Python programs.
 * A Python parser generated via ANTLRv4 is used for this purpose.
 * This implementation is based on ANTLRv4's Visitor pattern.
 *
 * @author Seyed Mohammad Ghaffarian
 */
public class PythonCFGBuilder {

	/**
	 * ‌Build and return the Control Flow Graph (CFG) for the given Java source file.
	 */
	public static ControlFlowGraph build(String pythonFile) throws IOException {
		return build(new File(pythonFile));
	}

	public static ControlFlowGraph build(InputStream inFile) throws IOException {
		// 将字符串转换为字节数组
//        byte[] bytes = content.getBytes();
		// 使用 ByteArrayInputStream 封装字节数组为输入流
//        InputStream inFile = new ByteArrayInputStream(bytes);

		ANTLRInputStream input = new ANTLRInputStream(inFile);
		PythonLexer lexer = new PythonLexer(input);
		CommonTokenStream tokens = new CommonTokenStream(lexer);
		PythonParser parser = new PythonParser(tokens);
		ParseTree tree = parser.root();
		return build(tree, null, null);
	}

	public static ControlFlowGraph build(String fileName, InputStream inputStream) throws IOException {

		InputStream inFile = inputStream;
		ANTLRInputStream input = new ANTLRInputStream(inFile);
		PythonLexer lexer = new PythonLexer(input);
		CommonTokenStream tokens = new CommonTokenStream(lexer);
		PythonParser parser = new PythonParser(tokens);
		ParseTree tree = parser.root();
		return build(fileName, tree, null, null);
	}

	/**
	 * ‌Build and return the Control Flow Graph (CFG) for the given Java source file.
	 */
	public static ControlFlowGraph build(File pythonFile) throws IOException {
		if (!pythonFile.getName().endsWith(".py"))
			throw new IOException("Not a Python File!");
		InputStream inFile = new FileInputStream(pythonFile);
		ANTLRInputStream input = new ANTLRInputStream(inFile);
		PythonLexer lexer = new PythonLexer(input);
		CommonTokenStream tokens = new CommonTokenStream(lexer);
		PythonParser parser = new PythonParser(tokens);
		ParseTree tree = parser.root();
		return build(pythonFile.getName(), tree, null, null);
	}

	public static ControlFlowGraph build(ParseTree tree, String propKey, Map<ParserRuleContext, Object> ctxProps) {
		ControlFlowGraph cfg = new ControlFlowGraph();
		ControlFlowVisitor visitor = new ControlFlowVisitor(cfg, propKey, ctxProps);
		visitor.visit(tree);
		return cfg;
	}

	/**
	 * ‌Build and return the Control Flow Graph (CFG) for the given Parse-Tree.
	 * The 'ctxProps' map includes contextual-properties for particular nodes
	 * in the parse-tree, which can be used for linking this graph with other
	 * graphs by using the same parse-tree and the same contextual-properties.
	 */
	public static ControlFlowGraph build(String pythonFileName, ParseTree tree,
										 String propKey, Map<ParserRuleContext, Object> ctxProps) {
		ControlFlowGraph cfg = new ControlFlowGraph(pythonFileName);
		ControlFlowVisitor visitor = new ControlFlowVisitor(cfg, propKey, ctxProps);
		visitor.visit(tree);
        return cfg;
    }

	/**
	 * Visitor-class which constructs the CFG by walking the parse-tree.
	 */
	private static class ControlFlowVisitor extends PythonParserBaseVisitor<Void> {

		private ControlFlowGraph cfg;
		private Deque<CFNode> preNodes;
		private Deque<CFEdge.Type> preEdges;
		private Deque<CFNode> preIfNodes;   //存每个判断的最后一个语句
		private Deque<CFEdge.Type> preIfEdges;
		private Deque<Block> loopBlocks;
		private List<Block> labeledBlocks;
		private Deque<Block> tryBlocks;
		private Queue<CFNode> casesQueue;
		private boolean dontPop;
		private String propKey;
		private Map<ParserRuleContext, Object> contexutalProperties;
		private Deque<String> classNames;

		public ControlFlowVisitor(ControlFlowGraph cfg, String propKey, Map<ParserRuleContext, Object> ctxProps) {
			preNodes = new ArrayDeque<>();
			preEdges = new ArrayDeque<>();
			preIfEdges = new ArrayDeque<>();
			preIfNodes = new ArrayDeque<>();
			loopBlocks = new ArrayDeque<>();
			labeledBlocks = new ArrayList<>();
			tryBlocks = new ArrayDeque<>();
			casesQueue = new ArrayDeque<>();
			classNames = new ArrayDeque<>();
			dontPop = false;
			this.cfg = cfg;
			//
			this.propKey = propKey;
			contexutalProperties = ctxProps;
		}

		/**
		 * Reset all data-structures and flags for visiting a new method declaration.
		 */
		private void init() {
			preNodes.clear();
			preEdges.clear();
			loopBlocks.clear();
			labeledBlocks.clear();
			tryBlocks.clear();
			dontPop = false;
		}

		/**
		 * Add contextual properties to the given node.
		 * This will first check to see if there is any property for the
		 * given context, and if so, the property will be added to the node.
		 */
		private void addContextualProperty(CFNode node, ParserRuleContext ctx) {
			if (propKey != null && contexutalProperties != null) {
				Object prop = contexutalProperties.get(ctx);
				if (prop != null)
					node.setProperty(propKey, prop);
			}
		}


		@Override
		public Void visitClass_or_func_def_stmt(PythonParser.Class_or_func_def_stmtContext ctx) {
			//| decorator* (classdef | funcdef)
			if (ctx.classdef() != null){
				classNames.push(ctx.classdef().name().getText());
				visit(ctx.classdef());
				classNames.pop();
			}
			if (ctx.funcdef() != null){
				visit(ctx.funcdef());
			}
			return null;
		}

		// 不解析类，只解析函数，因此只需要记录类名就行了
        @Override
        public Void visitClassdef(PythonParser.ClassdefContext ctx) {
			//   : CLASS name (OPEN_PAREN arglist? CLOSE_PAREN)? COLON suite
			//    ;
            try{
                init();
                CFNode entry = new CFNode();       				  //构建类头节点
                entry.setLineOfCode(ctx.getStart().getLine());
				if (ctx.arglist() != null)
                	entry.setCode(ctx.CLASS().getText() + ' ' + ctx.name().getText() + '(' + getOriginalCodeText(ctx.arglist()) + "):");
                else
					entry.setCode(ctx.CLASS().getText() + ' ' + ctx.name().getText() + "():");
				addContextualProperty(entry, ctx);      //加入对应的上下文属性
                cfg.addVertex(entry);
                entry.setProperty("class", classNames.peek());
                preNodes.push(entry);
                preEdges.push(CFEdge.Type.EPSILON);
            }catch (Exception e){
                e.printStackTrace();
            }
			return visitChildren(ctx);
        }

		@Override
		public Void visitFuncdef(PythonParser.FuncdefContext ctx) {
			//  : ASYNC? DEF name OPEN_PAREN typedargslist? CLOSE_PAREN (ARROW test)? COLON suite
			//    ;
			try {
				init();
				CFNode entry = new CFNode();
				entry.setLineOfCode(ctx.getStart().getLine());
				String retType = "void";
				if (ctx.test() != null)     // set return type
					retType = ctx.test().getText();
				String precode = (ctx.ASYNC() != null) ? ctx.ASYNC().getText() + ctx.DEF().getText() + ' ' + ctx.name().getText() : ctx.DEF().getText() + ' ' + ctx.name().getText();
                precode = (ctx.typedargslist() != null) ? precode + '(' + ctx.typedargslist().getText() + ')' : precode + "()";
				precode = (ctx.ARROW() != null) ? precode + ctx.ARROW().getText() : precode;
				precode = (ctx.test() != null) ? precode + ctx.test().getText() + ctx.COLON().getText() : precode + ctx.COLON().getText();
				entry.setCode(precode);
				addContextualProperty(entry, ctx);
				cfg.addVertex(entry);
				entry.setProperty("name", ctx.name().getText());
				entry.setProperty("class", classNames.peek());
				entry.setProperty("type", retType);
				cfg.addMethodEntry(entry);
				preNodes.push(entry);
				preEdges.push(CFEdge.Type.EPSILON);
			}catch (Exception e){
				e.printStackTrace();
			}
			return visitChildren(ctx);
		}

		@Override
		public Void visitExpr_stmt(PythonParser.Expr_stmtContext ctx) {
			//: testlist_star_expr assign_part?
			CFNode declr = new CFNode();
			declr.setLineOfCode(ctx.getStart().getLine());
			declr.setCode(getOriginalCodeText(ctx));
			addContextualProperty(declr, ctx);
			addNodeAndPreEdge(declr);             //加顺序边
			preEdges.push(CFEdge.Type.EPSILON);
			preNodes.push(declr);
			return null;
		}

		@Override
		public Void visitTry_stmt(PythonParser.Try_stmtContext ctx) {
			// TRY COLON suite (except_clause+ else_clause? finally_clause? | finally_clause)
			CFNode tryNode = new CFNode();
			tryNode.setLineOfCode(ctx.getStart().getLine());
			tryNode.setCode("try:");
			addContextualProperty(tryNode, ctx);
			addNodeAndPreEdge(tryNode);
			//
			CFNode endTry = new CFNode();
			endTry.setLineOfCode(0);
			endTry.setCode("end-try");
			cfg.addVertex(endTry);
			//
			preEdges.push(CFEdge.Type.EPSILON);
			preNodes.push(tryNode);
			tryBlocks.push(new Block(tryNode, endTry));//解决raise的问题
			visit(ctx.suite());
			CFNode finallTryNodes = null;
			if (preNodes != null && preNodes.size() != 0) {
				finallTryNodes = preNodes.peek();
			}                                        //记录try block的最后一个statement
			popAddPreEdgeTo(endTry);
			// If there is a finally-block, visit it first
			CFNode finallyNode = null;
			CFNode endFinally = null;
			if (ctx.finally_clause() != null) {
				// 'finally' block
				finallyNode = new CFNode();
				finallyNode.setLineOfCode(ctx.finally_clause().getStart().getLine());
				finallyNode.setCode("finally:");
				addContextualProperty(finallyNode, ctx.finally_clause());
				cfg.addVertex(finallyNode);
				cfg.addEdge(new Edge<>(endTry, new CFEdge(CFEdge.Type.EPSILON), finallyNode));
				//
				preEdges.push(CFEdge.Type.EPSILON);
				preNodes.push(finallyNode);
				visit(ctx.finally_clause().suite());
				//
				endFinally = new CFNode();
				endFinally.setLineOfCode(0);
				endFinally.setCode("end-finally");
				addNodeAndPreEdge(endFinally);
			}
			// deal with the except
			if (ctx.except_clause() != null && ctx.except_clause().size() != 0){
				CFNode endExcept = new CFNode();
				endExcept.setLineOfCode(0);
				endExcept.setCode("end-except");
				cfg.addVertex(endExcept);
				// EXCEPT (test ({this.CheckVersion(2)}? COMMA name {this.SetVersion(2);} | {this.CheckVersion(3)}? AS name {this.SetVersion(3);})?)? COLON suite
				for(int i = 0; i < ctx.except_clause().size(); i++){
					PythonParser.Except_clauseContext temctx = ctx.except_clause(i);
					CFNode exceptNode = new CFNode();
					exceptNode.setLineOfCode(temctx.getStart().getLine());
					if (temctx.test() != null) {
						String code = "except " + temctx.test().getText();
						code = (temctx.AS() != null) ? code + " as " + temctx.name().getText() + ":" : code + ":";
						exceptNode.setCode(code);
					} else {
						String code = "except:";
						exceptNode.setCode(code);
					}
					addContextualProperty(exceptNode, temctx);
					cfg.addVertex(exceptNode);
					cfg.addEdge(new Edge<>(endTry, new CFEdge(CFEdge.Type.RAISE), exceptNode));
					//
					preEdges.push(CFEdge.Type.EPSILON);
					preNodes.push(exceptNode);
					visit(temctx.suite());
					popAddPreEdgeTo(endExcept);
				}
				// deal with the else node
				if (ctx.else_clause() != null){
					preNodes.push(finallTryNodes); preEdges.push(CFEdge.Type.EPSILON);
					visit(ctx.else_clause());
					if (preNodes != null && preEdges != null && preEdges.size() != 0 && preNodes.size() != 0) {
						cfg.addEdge(new Edge<>(preNodes.pop(), new CFEdge(CFEdge.Type.EPSILON), endTry));   // connect the last node of the else block to Try node
					}
				}
				// connect end-except node to finally-node,
				// and push end-except to the stack ...
				if (ctx.finally_clause() != null){
					cfg.addEdge(new Edge<>(endExcept, new CFEdge(CFEdge.Type.EPSILON), finallyNode));
					preEdges.push(CFEdge.Type.EPSILON);
					preNodes.push(endFinally);
				}else{
					// connect end-except node to end-try,
					// and push end-try to the the stack ...
					cfg.addEdge(new Edge<>(endExcept, new CFEdge(CFEdge.Type.EPSILON), endTry));
					preEdges.push(CFEdge.Type.EPSILON);
					preNodes.push(endTry);
				}
			}else {
				// No except-clause; it's a try-else-finally
				// push end-finally to the stack ...
				// judge the else block
				if(ctx.else_clause() != null){
					preNodes.push(finallTryNodes); preEdges.push(CFEdge.Type.EPSILON);
					visit(ctx.else_clause());
					if (preNodes != null && preEdges != null && preNodes.size() != 0 && preEdges.size() != 0) {
						cfg.addEdge(new Edge<>(preNodes.pop(), new CFEdge(CFEdge.Type.EPSILON), endTry));
					}
					if (ctx.finally_clause() != null){
						preEdges.push(CFEdge.Type.EPSILON);
						preNodes.push(endFinally);
					}else{
						//no execpt; no finally
						preEdges.push(CFEdge.Type.EPSILON);
						preNodes.push(endTry);
					}
				}else{
					// No except-clause and else-clause; it's a try-finally
					// push end-finally to the stack ...
					// judge the else block
					if (ctx.finally_clause() != null){
						preEdges.push(CFEdge.Type.EPSILON); preNodes.push(endFinally);
					}else{
						// just try
						preEdges.push(CFEdge.Type.EPSILON); preNodes.push(endTry);
					}
				}
			}
			// NOTE that Java does not allow a singular try-block (without catch or finally)
			return null;
		}

		@Override
		public Void visitWith_stmt(PythonParser.With_stmtContext ctx) {
			// ASYNC? WITH with_item (COMMA with_item)* COLON suite
			// deal with the with node
			CFNode withNode = new CFNode();
			withNode.setLineOfCode(ctx.getStart().getLine());
			String code = (ctx.ASYNC()!=null) ? "async with " : "with ";
			code = code + tranWithItem(ctx.with_item(0)) + " ";
			if (ctx.with_item().size() > 1){
				for (int i = 1; i < ctx.with_item().size(); i++){
					code = code + ", " + tranWithItem(ctx.with_item(i));
				}
				code = code + ":";
			}
			withNode.setCode(code);
			addContextualProperty(withNode, ctx);
			addNodeAndPreEdge(withNode);
			/* create the endwith node */
			CFNode endwith = new CFNode();
			endwith.setLineOfCode(0);
			endwith.setCode("endwith");
			cfg.addVertex(endwith);
			cfg.addEdge(new Edge<>(withNode, new CFEdge(CFEdge.Type.FALSE), endwith));
			preEdges.push(CFEdge.Type.TRUE);
			preNodes.push(withNode);
			visit(ctx.suite());
			if (preNodes != null && preEdges != null && preNodes.size() != 0 && preEdges.size() != 0) {
				cfg.addEdge(new Edge<>(preNodes.pop(), new CFEdge(CFEdge.Type.EPSILON), endwith));
				preEdges.pop();
			}
			preEdges.push(CFEdge.Type.EPSILON); preNodes.push(endwith);
			return null;
		}

		@Override
		public Void visitPrint_stmt(PythonParser.Print_stmtContext ctx) {
			CFNode print = new CFNode();
			print.setLineOfCode(ctx.getStart().getLine());
			print.setCode(getOriginalCodeText(ctx));
			addContextualProperty(print, ctx);
			addNodeAndPreEdge(print);             //加顺序边
			preEdges.push(CFEdge.Type.EPSILON);
			preNodes.push(print);
			return null;
		}

		@Override
		public Void visitDel_stmt(PythonParser.Del_stmtContext ctx) {
			CFNode del = new CFNode();
			del.setLineOfCode(ctx.getStart().getLine());
			del.setCode(getOriginalCodeText(ctx));
			addContextualProperty(del, ctx);
			addNodeAndPreEdge(del);             //加顺序边
			preEdges.push(CFEdge.Type.EPSILON);
			preNodes.push(del);
			return null;
		}

		@Override
		public Void visitPass_stmt(PythonParser.Pass_stmtContext ctx) {
			CFNode pass = new CFNode();
			pass.setLineOfCode(ctx.getStart().getLine());
			pass.setCode(getOriginalCodeText(ctx));
			addContextualProperty(pass, ctx);
			addNodeAndPreEdge(pass);             //加顺序边
			preEdges.push(CFEdge.Type.EPSILON);
			preNodes.push(pass);
			return null;
		}

		@Override
		public Void visitBreak_stmt(PythonParser.Break_stmtContext ctx) {
			CFNode breakNode = new CFNode();
			breakNode.setLineOfCode(ctx.getStart().getLine());
			breakNode.setCode(getOriginalCodeText(ctx));
			addContextualProperty(breakNode, ctx);
			addNodeAndPreEdge(breakNode);
			Block block = loopBlocks.peek();
			cfg.addEdge(new Edge<>(breakNode, new CFEdge(CFEdge.Type.EPSILON), block.end)); //增加break到endfor节点
			dontPop = true;          //设置不被弹出栈,因为自己设置了一次边导致的，它后面不会有其他的边，因此要重新开始设置一次边
			return null;
		}

		@Override
		public Void visitContinue_stmt(PythonParser.Continue_stmtContext ctx) {
			CFNode continueNode = new CFNode();
			continueNode.setLineOfCode(ctx.getStart().getLine());
			continueNode.setCode(getOriginalCodeText(ctx));
			addContextualProperty(continueNode, ctx);
			addNodeAndPreEdge(continueNode);
			Block block = loopBlocks.peek();
			cfg.addEdge(new Edge<>(continueNode, new CFEdge(CFEdge.Type.EPSILON), block.start));  //此处逻辑不同break
			dontPop = true;
			return null;
		}

		@Override
		public Void visitReturn_stmt(PythonParser.Return_stmtContext ctx) {
			//| RETURN testlist?
			CFNode ret = new CFNode();
			ret.setLineOfCode(ctx.getStart().getLine());
			ret.setCode(getOriginalCodeText(ctx));
			addContextualProperty(ret, ctx);
			addNodeAndPreEdge(ret);
			dontPop = true;
			return null;
		}

		@Override
		public Void visitRaise_stmt(PythonParser.Raise_stmtContext ctx) {
			//| RAISE (test (COMMA test (COMMA test)?)?)? (FROM test)?
			CFNode raiseNode = new CFNode();
			raiseNode.setLineOfCode(ctx.getStart().getLine());
			if (ctx.test(0) != null) {
				String code = "raise " + ctx.test(0).getText() + " ";
				code = (ctx.FROM() != null) ? code + "from " + ctx.test(1).getText() : code;
				raiseNode.setCode(code);
			} else {
				String code = "raise";
				raiseNode.setCode(code);
			}
			addContextualProperty(raiseNode, ctx);
			addNodeAndPreEdge(raiseNode);
			preEdges.push(CFEdge.Type.EPSILON);
			preNodes.push(raiseNode);
			//
			if (!tryBlocks.isEmpty()) {
				Block tryBlock = tryBlocks.peek();
				cfg.addEdge(new Edge<>(raiseNode, new CFEdge(CFEdge.Type.RAISE), tryBlock.end));
			} else {
				// do something when it's a throw not in a try-catch block ...
				// in such a situation, the method declaration has a throws clause;
				// so we should create a special node for the method-throws,
				// and create an edge from this throw-statement to that throws-node.
			}
			dontPop = true;
			return null;
		}

		@Override
		public Void visitYield_stmt(PythonParser.Yield_stmtContext ctx) {
			//  : YIELD yield_arg?
			//    ;
			CFNode yieldNode = new CFNode();
			yieldNode.setLineOfCode(ctx.getStart().getLine());
			yieldNode.setCode(ctx.getText());
			addContextualProperty(yieldNode, ctx);
			addNodeAndPreEdge(yieldNode);             //加顺序边
			preEdges.push(CFEdge.Type.EPSILON);
			preNodes.push(yieldNode);
			return null;
		}

		@Override
		public Void visitImport_stmt(PythonParser.Import_stmtContext ctx) {
			//| IMPORT dotted_as_names 考虑到方法内部也可以import,因此采用该方法
			CFNode imp = new CFNode();
			imp.setLineOfCode(ctx.getStart().getLine());
			String code = "import ";

			for (int i = 0; i < ctx.dotted_as_names().dotted_as_name().size(); i++){
				if (i == 0){
					code = code + ctx.dotted_as_names().dotted_as_name(i).getText();
				}else {
					code = code + ", " + ctx.dotted_as_names().dotted_as_name(i).getText();
				}
			}
			imp.setCode(code);
			addContextualProperty(imp, ctx);
			addNodeAndPreEdge(imp);             //加顺序边
			preEdges.push(CFEdge.Type.EPSILON);
			preNodes.push(imp);
			return null;
		}

		@Override
		public Void visitFrom_stmt(PythonParser.From_stmtContext ctx) {
			//FROM ((DOT | ELLIPSIS)* dotted_name | (DOT | ELLIPSIS)+)
			//      IMPORT (STAR | OPEN_PAREN import_as_names CLOSE_PAREN | import_as_names)
			//import_as_names
			//    : import_as_name (COMMA import_as_name)* COMMA?
			//    ;import_as_name
			//    : name (AS name)?
			//    ;
			CFNode from = new CFNode();
			String code = "from ";
			for(int i = 0; i < ctx.DOT().size(); i++){
				code = code + ctx.DOT(i).getText();
			}
			code = (ctx.dotted_name() != null) ? code + ctx.dotted_name().getText() : code;
			code = code + " import ";
			if (ctx.STAR() != null){
				code = code + ctx.STAR().getText();
			}else{
				if (ctx.import_as_names() != null){
					assert ctx.import_as_names() instanceof PythonParser.Import_as_namesContext;
					code = (ctx.OPEN_PAREN() != null) ? code + '(' : code;
					int com_length = ctx.import_as_names().import_as_name().size();
					for (int i = 0; i < ctx.import_as_names().import_as_name().size(); i++){
						PythonParser.Import_as_nameContext temAsName = ctx.import_as_names().import_as_name(i);
						code = (temAsName.AS() != null) ? code + temAsName.name(0).getText() + " as " + temAsName.name(1).getText() : code + temAsName.name(0).getText();
						if (--com_length != 0) code = code + ',';
					}
				}else{
					//这种情况不存在
				}
			}
			from.setCode(code);
			addContextualProperty(from, ctx);
			addNodeAndPreEdge(from);             //加顺序边
			preEdges.push(CFEdge.Type.EPSILON);
			preNodes.push(from);
			return null;
		}

		@Override
		public Void visitGlobal_stmt(PythonParser.Global_stmtContext ctx) {
			//| GLOBAL name (COMMA name)*
			CFNode glb = new CFNode();
			glb.setLineOfCode(ctx.getStart().getLine());
			assert ctx.COMMA().size() == ctx.name().size() - 1;
			String code = "global " + ctx.name(0).getText();
			if (ctx.COMMA().size() != 0 && ctx.COMMA() != null){
				int comNum = ctx.COMMA().size();
				for (int i = 0; i < comNum; i++){
					code = code + ',' + ctx.name(i+1).getText();
				}
			}
//			System.out.println(code);
			glb.setCode(code);
			addContextualProperty(glb, ctx);
			addNodeAndPreEdge(glb);             //加顺序边
			preEdges.push(CFEdge.Type.EPSILON);
			preNodes.push(glb);
			return null;
		}

		@Override
		public Void visitExec_stmt(PythonParser.Exec_stmtContext ctx) {
			//{this.CheckVersion(2)}? EXEC expr (IN test (COMMA test)?)? {this.SetVersion(2);}
			CFNode exec = new CFNode();
			exec.setLineOfCode(ctx.getStart().getLine());
			String code = "exec " + ctx.expr().getText();
			if (ctx.IN() != null) code = code + " in " + ctx.test(0).getText();
			for (int i = 1; i < ctx.test().size(); i++){
				code = code + ", " + ctx.test(i).getText();
			}
			System.out.println(code);
			exec.setCode(getOriginalCodeText(ctx));
			addContextualProperty(exec, ctx);
			addNodeAndPreEdge(exec);             //加顺序边
			preEdges.push(CFEdge.Type.EPSILON);
			preNodes.push(exec);
			return null;
		}

		@Override
		public Void visitAssert_stmt(PythonParser.Assert_stmtContext ctx) {
			//ASSERT test (COMMA test)?
			CFNode Asst = new CFNode();
			Asst.setLineOfCode(ctx.getStart().getLine());
			String code = "assert " + ctx.test(0).getText();
			for (int i = 1; i < ctx.test().size(); i++){
				code = code + ", " + ctx.test(i).getText();
			}
			Asst.setCode(code);
			addContextualProperty(Asst, ctx);
			addNodeAndPreEdge(Asst);             //加顺序边
			preEdges.push(CFEdge.Type.EPSILON);
			preNodes.push(Asst);
			return null;
		}

		@Override
		public Void visitNonlocal_stmt(PythonParser.Nonlocal_stmtContext ctx) {
			//{this.CheckVersion(3)}? NONLOCAL name (COMMA name)* {this.SetVersion(3);}
			CFNode nonlocal = new CFNode();
			nonlocal.setLineOfCode(ctx.getStart().getLine());
			String code = "nonlocal " + ctx.name(0).getText();
			for (int i = 1; i < ctx.name().size(); i++){
				code = code + ", " + ctx.name(i).getText();
			}
			nonlocal.setCode(code);
			addContextualProperty(nonlocal, ctx);
			addNodeAndPreEdge(nonlocal);             //加顺序边
			preEdges.push(CFEdge.Type.EPSILON);
			preNodes.push(nonlocal);
			return null;
		}

		@Override
		public Void visitIf_stmt(PythonParser.If_stmtContext ctx) {
			//IF cond=test COLON suite elif_clause* else_clause?
			CFNode ifNode = new CFNode();
			ifNode.setLineOfCode(ctx.getStart().getLine());
			String code = "if " + ctx.test().getText() + ctx.COLON().getText();
			ifNode.setCode(code);
			addContextualProperty(ifNode, ctx);
			addNodeAndPreEdge(ifNode);
			/* deal with the true situation of if statement */
			preEdges.push(CFEdge.Type.TRUE);
			preNodes.push(ifNode);
			visit(ctx.suite());
			// Store the last node of the if block of the true situation
			if (preEdges != null && preNodes != null && preNodes.size() != 0 && preEdges.size() != 0) {
				preIfEdges.push(preEdges.pop());
				preIfNodes.push(preNodes.pop());
			}
			/* deal with the situation of elif */
			if (ctx.elif_clause() != null && ctx.elif_clause().size() != 0){
				for (int i = 0; i < ctx.elif_clause().size(); i++){
					PythonParser.Elif_clauseContext temCtx = ctx.elif_clause(i);
					CFNode elifNode = new CFNode();
					elifNode.setLineOfCode(temCtx.getStart().getLine());
					String elifcode = "elif " + temCtx.test().getText() + temCtx.COLON().getText();
					elifNode.setLineOfCode(ctx.elif_clause(i).getStart().getLine());
					elifNode.setCode(elifcode);
					addContextualProperty(elifNode, temCtx);
					cfg.addVertex(elifNode);
					//增加if 到 elif的边
					if (i == 0){
						cfg.addEdge(new Edge<>(ifNode, new CFEdge(CFEdge.Type.FALSE), elifNode));
						dontPop = true;
					}else{
						//增加上个elif到下个elif的边
						if (preIfNodes != null && preIfNodes.size() != 0) {
							cfg.addEdge(new Edge<>(preIfNodes.pop(), new CFEdge(CFEdge.Type.FALSE), elifNode));
							dontPop = true;
						}
					}
					addNodeAndPreEdge(elifNode);
					preEdges.push(CFEdge.Type.TRUE);
					preNodes.push(elifNode);
					preIfNodes.push(elifNode);      //暂存上个elif节点
					visit(temCtx.suite());
					// 最后一个elif节点
					if (i == ctx.elif_clause().size() - 1){
						if (preIfNodes != null && preIfNodes.size() != 0) {
							preIfNodes.pop();          //将最后没有用到的elif节点弹出
						}
						if (preEdges != null && preNodes != null && preNodes.size() != 0 && preEdges.size() != 0) {
							preIfNodes.push(preNodes.pop());  //将上一个逻辑的最后的那个语句压入
							preIfEdges.push(preEdges.pop());
						}
					}else{
						if (preEdges != null && preNodes != null && preNodes.size() != 0 && preEdges.size() != 0) {
							preEdges.pop();
							preNodes.pop();
						}
					}
				}/* deal with the if elif else statement */
				if (ctx.else_clause() != null){
					if (preIfEdges.peek() != null) {
						preNodes.push(preIfNodes.peek());
						preEdges.push(CFEdge.Type.FALSE);
					}
					visit(ctx.else_clause().suite());
					if (preEdges != null && preNodes != null && preNodes.size() != 0 && preEdges.size() != 0) {
						preIfEdges.push(preEdges.pop());
						preIfNodes.push(preNodes.pop());
					}
				}
			}else{/* deal with the if  else statement */
				if (ctx.else_clause() != null){
					preNodes.push(ifNode); preEdges.push(CFEdge.Type.FALSE);
					visit(ctx.else_clause().suite());
					if (preEdges != null && preNodes != null && preNodes.size() != 0 && preEdges.size() != 0) {
						preIfNodes.push(preNodes.pop());
						preIfEdges.push(preEdges.pop());
					}
				}
			}
			CFNode endif = new CFNode();
			endif.setLineOfCode(0);
			endif.setCode("endif");
			cfg.addVertex(endif);
			//所有逻辑块最后一个语句与endif相接
			if (preIfNodes != null && preIfNodes.size() != 0) {
				while (!preIfNodes.isEmpty()) {
					cfg.addEdge(new Edge<>(preIfNodes.pop(), new CFEdge(CFEdge.Type.EPSILON), endif));
				}
			}
			preEdges.push(CFEdge.Type.EPSILON);
			preNodes.push(endif);
			return null;
		}

		@Override
		public Void visitWhile_stmt(PythonParser.While_stmtContext ctx) {
			// WHILE test COLON suite else_clause?
			CFNode whileNode = new CFNode();
			whileNode.setLineOfCode(ctx.getStart().getLine());
			whileNode.setCode("while " + getOriginalCodeText(ctx.test()) + ctx.COLON().getText());
			addContextualProperty(whileNode, ctx);
			addNodeAndPreEdge(whileNode);
			/* create the endwhile node */
			CFNode endwhile = new CFNode();
			endwhile.setLineOfCode(0);
			endwhile.setCode("endwhile");
			cfg.addVertex(endwhile);
			cfg.addEdge(new Edge<>(whileNode, new CFEdge(CFEdge.Type.FALSE), endwhile));
			preNodes.push(whileNode);
			preEdges.push(CFEdge.Type.TRUE);
			loopBlocks.push(new Block(whileNode, endwhile));
			visit(ctx.suite());
			loopBlocks.pop();
			popAddPreEdgeTo(whileNode);
			/* deal with the else block */
			if (ctx.else_clause() != null){
				preNodes.push(whileNode); preEdges.push(CFEdge.Type.FALSE);
				visit(ctx.else_clause().suite());
			}else{
				preEdges.push(CFEdge.Type.EPSILON); preNodes.push(endwhile);
			}
			return null;
		}

		@Override
		public Void visitFor_stmt(PythonParser.For_stmtContext ctx) {
			// ASYNC? FOR exprlist IN testlist COLON suite else_clause?
			CFNode forNode = new CFNode();
			String code = (ctx.ASYNC() != null) ? "async for "+ctx.exprlist().getText()+" in "+ctx.testlist().getText()+":" : "for "+ctx.exprlist().getText()+" in "+ctx.testlist().getText()+":";
			forNode.setLineOfCode(ctx.getStart().getLine());
			forNode.setCode(code);
			addContextualProperty(forNode, ctx);
			addNodeAndPreEdge(forNode);
			/* build the endfor node  */
			CFNode forEnd = new CFNode();
			forEnd.setLineOfCode(0);
			forEnd.setCode("endfor");
			cfg.addVertex(forEnd);
			cfg.addEdge(new Edge<>(forNode, new CFEdge(CFEdge.Type.FALSE), forEnd));
			// deal with the loop
			preEdges.push(CFEdge.Type.TRUE);
			preNodes.push(forNode);
			loopBlocks.push(new Block(forNode, forEnd));
			visit(ctx.suite());
			loopBlocks.pop();
			CFNode temNode = preNodes.peek();
			popAddPreEdgeTo(forNode);   //保持循环的特性，加入一条循环的边
			//deal with the else block
			if (ctx.else_clause() != null){
				preNodes.push(forNode); preEdges.push(CFEdge.Type.FALSE);
				visit(ctx.else_clause().suite());
			}else{
				preEdges.push(CFEdge.Type.EPSILON);
				preNodes.push(temNode);
			}
			return null;
		}

		/**
		 * Get resulting Control-Flow-Graph of this CFG-Builder.
		 */
		public ControlFlowGraph getCFG() {
			return cfg;
		}

		/**
		 * Add this node to the CFG and create edge from pre-node to this node.
		 */
		private void addNodeAndPreEdge(CFNode node) {
			cfg.addVertex(node);
			popAddPreEdgeTo(node);
		}

		/**
		 * Add a new edge to the given node, by poping the edge-type of the stack.
		 */
		private void popAddPreEdgeTo(CFNode node) {
			if (dontPop)
				dontPop = false;
			else {
				Logger.debug("\nPRE-NODES = " + preNodes.size());
				Logger.debug("PRE-EDGES = " + preEdges.size() + '\n');
				if (preNodes != null && preEdges != null && preEdges.size() != 0 && preNodes.size() != 0) {
					cfg.addEdge(new Edge<>(preNodes.pop(), new CFEdge(preEdges.pop()), node));
				}
			}
			//
			for (int i = casesQueue.size(); i > 0; --i)
				cfg.addEdge(new Edge<>(casesQueue.remove(), new CFEdge(CFEdge.Type.TRUE), node));
		}

		/**
		 * Get the original program text for the given parser-rule context.
		 * This is required for preserving whitespaces.
		 */
		private String getOriginalCodeText(ParserRuleContext ctx) {
			int start = ctx.start.getStartIndex();
			int stop = ctx.stop.getStopIndex();
			Interval interval = new Interval(start, stop);
			return ctx.start.getInputStream().getText(interval);
		}
		/**
		 * transfor the with statement to String
		 */
		private String tranWithItem(ParserRuleContext ctx){
			assert ctx instanceof PythonParser.With_itemContext;
			String code = ((PythonParser.With_itemContext) ctx).test().getText();
			if (((PythonParser.With_itemContext) ctx).AS() != null){
				code = code + " as " + ((PythonParser.With_itemContext) ctx).expr().getText();
			}
			return code;
		}
		/**
		 * A simple structure for holding the start, end, and label of code blocks.
		 * These are used to handle 'break' and 'continue' statements.
		 */
		private class Block {

			public final String label;
			public final CFNode start, end;

			Block(CFNode start, CFNode end, String label) {
				this.start = start;
				this.end = end;
				this.label = label;
			}

			Block(CFNode start, CFNode end) {
				this(start, end, "");
			}
		}
	}
}
